import { Consumer } from '../../Function'
import { ArrayTool } from '../../tool/ArrayTool'
import { ObjectTool } from '../../tool/ObjectTool'
import { Event } from '../Event'
import { EventCenter } from '../EventCenter'
import { EventRegistry } from '../EventRegistry'

// noinspection JSUnusedGlobalSymbols
/**
 *  SimpleEventCenter
 * @author LL
 * @date 2022/1/27 18:00
 */
export class SimpleEventCenter<R extends EventRegistry> implements EventCenter<R> {
    private container: Record<keyof R, Array<Consumer<Event> | null>> = {} as any
    private tempContainer: Record<keyof R, Array<Consumer<Event> | null>> = {} as any

    public push(event: Event): void {
        this.dispatch(event, this.container, false)
        this.dispatch(event, this.tempContainer, true)
    }

    public removeEventListener<K extends keyof R>(name: K, id: any): void {
        this.remove(name, id, this.container)
        this.remove(name, id, this.tempContainer)
    }

    public addEventListener<K extends keyof R>(name: K, callback: Consumer<R[K]>): number {
        return this.addListener(name, callback, this.container)
    }

    public addOnceEventListener<K extends keyof R>(name: K, callback: Consumer<R[K]>): number {
        return this.addListener(name, callback, this.tempContainer)
    }

    /**
     * @param event 发生的事件
     * @param {Record<keyof R, Array<Consumer<Event>>>} container 事件监听器容器
     * @param {boolean} isOnce 仅仅调用一次
     */
    private dispatch(event: Event, container: Record<keyof R, Array<Consumer<Event> | null>>, isOnce: boolean) {
        let name = event.getName()
        // @ts-ignore
        const consumers: Array<Consumer<Event> | null> = container[name]
        if (ArrayTool.isEmpty(consumers)) {
            return
        }
        let notNullConsumers = consumers.filter(i => ObjectTool.isNotNull(i))
        for (const consumer of notNullConsumers) {
            try {
                consumer?.(event)
            } catch (e) {
                console.debug('事件回调错误: ', e)
            }
        }
        if (isOnce) {
            // @ts-ignore
            delete consumers[name]
        }
    }

    /**
     *
     * @param {K} name
     * @param id
     * @param {Record<keyof R, Array<Consumer<Event> | null>>} container
     * @private
     */
    private remove<K extends keyof R>(name: K, id: any, container: Record<keyof R, Array<Consumer<Event> | null>>) {
        const list = container[name]
        if (ArrayTool.isEmpty(list)) {
            return
        }
        list[id] = null
    }

    private addListener<K extends keyof R>(name: K, callback: Consumer<R[K]>, container: Record<keyof R, Array<Consumer<Event> | null>>) {
        const list = container[name]
        if (ArrayTool.isEmpty(list)) {
            // @ts-ignore
            container[name] = [ callback ]
            return 0
        }
        // @ts-ignore
        return list.push(callback)
    }

}
